#include "softi2c.h"
#include "main.h"
#include "stm32h7xx_hal.h"
#include "stm32h7xx_hal_def.h"
#include "system_stm32h7xx.h"
#include <stdint.h>

static inline void _sda_output(SoftwareI2C_HandleTypeDef* hi2c);
static inline void _sda_input(SoftwareI2C_HandleTypeDef* hi2c);
static inline void _scl_high(SoftwareI2C_HandleTypeDef* hi2c);
static inline void _scl_low(SoftwareI2C_HandleTypeDef* hi2c);
static inline void _sda_high(SoftwareI2C_HandleTypeDef* hi2c);
static inline void _sda_low(SoftwareI2C_HandleTypeDef* hi2c);

// 初始化软件I2C
void SoftI2C_Init(SoftwareI2C_HandleTypeDef* hi2c)
{
     // 使能GPIO时钟
    if(hi2c->scl_port == GPIOA) {
        __HAL_RCC_GPIOA_CLK_ENABLE();
    } else if (hi2c->scl_port == GPIOB) {
        __HAL_RCC_GPIOB_CLK_ENABLE();
    } else if (hi2c->scl_port == GPIOC) {
        __HAL_RCC_GPIOC_CLK_ENABLE();
    } else if (hi2c->scl_port == GPIOD) {
        __HAL_RCC_GPIOD_CLK_ENABLE();
    } else if (hi2c->scl_port == GPIOF) {
        __HAL_RCC_GPIOF_CLK_ENABLE();
    } else if (hi2c->scl_port == GPIOG) {
        __HAL_RCC_GPIOG_CLK_ENABLE();
    } else if (hi2c->scl_port == GPIOH) {
        __HAL_RCC_GPIOH_CLK_ENABLE();
    }
    // 配置SCL和SDA引脚为输出模式
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin = hi2c->scl_pin | hi2c->sda_pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(hi2c->scl_port, &GPIO_InitStruct);

    // 设置初始状态为高电平
    HAL_GPIO_WritePin(hi2c->scl_port, hi2c->scl_pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(hi2c->sda_port, hi2c->sda_pin, GPIO_PIN_SET);

    // 计算延时值（1kHz，周期1ms，高低电平各占0.5ms）
    hi2c->delay_us = 1000000 / (hi2c->speed * 2); // 微秒级延时
}
void SoftI2C_Start(SoftwareI2C_HandleTypeDef* hi2c)
{
    // 产生起始信号
    _sda_output(hi2c); // 设置SDA为输出
    _scl_high(hi2c); // SCL高电平
    _sda_high(hi2c); // SDA高电平
    // delay_us(hi2c->delay_us); // 等待一段时间
    _sda_low(hi2c); // SDA拉低
    delay_us(hi2c->delay_us); // 等待一段时间
}
void SoftI2C_Stop(SoftwareI2C_HandleTypeDef* hi2c)
{
    _sda_output(hi2c); // 设置SDA为输出
    _scl_high(hi2c); // SCL拉高
    _sda_low(hi2c); // SDA拉低
    // delay_us(hi2c->delay_us); // 等待一段时间
    _sda_high(hi2c); // SDA拉高
    delay_us(hi2c->delay_us); // 等待一段时间
}

uint8_t SoftI2C_WaitAck(SoftwareI2C_HandleTypeDef* hi2c, uint32_t timeout)
{
    uint32_t startTicks = HAL_GetTick();
    _sda_high(hi2c);
    _sda_input(hi2c);
    _scl_high(hi2c);
    delay_us(hi2c->delay_us); // 等待一段时间
    while(HAL_GPIO_ReadPin(hi2c->sda_port, hi2c->sda_pin)==GPIO_PIN_SET){
        if(HAL_GetTick() - startTicks > timeout) {
            _scl_low(hi2c); // 超时后拉低SCL
            _sda_output(hi2c); // 重新设置SDA为输出
            return HAL_TIMEOUT; // 返回超时状态
        }
    }
    _scl_low(hi2c); // SCL拉低
    delay_us(hi2c->delay_us); // 等待一段时间
    _sda_output(hi2c); // 重新设置SDA为输出
    return HAL_OK; // 返回ACK状态
}

void SoftI2C_SendAck(SoftwareI2C_HandleTypeDef* hi2c)
{
    _sda_output(hi2c);
    _scl_low(hi2c);

    _sda_low(hi2c); // 发送ACK
    delay_us(hi2c->delay_us); // 等待一段时间

    _scl_high(hi2c); // SCL拉高
    delay_us(hi2c->delay_us); // 等待一段时间
    _scl_low(hi2c); // SCL拉低
}

void SoftI2C_SendNAck(SoftwareI2C_HandleTypeDef* hi2c)
{
    _sda_output(hi2c);
    _scl_low(hi2c);

    _sda_high(hi2c); // 发送NACK
    delay_us(hi2c->delay_us); // 等待一段时间
    
    _scl_high(hi2c); // SCL拉高
    delay_us(hi2c->delay_us); // 等待一段时间
    _scl_low(hi2c); // SCL拉低
}

uint8_t SoftI2C_WriteByte(SoftwareI2C_HandleTypeDef* hi2c, uint8_t byte)
{
    _sda_output(hi2c);
    _scl_low(hi2c);
    for (int i = 0; i < 8; i++) {
        if (byte & 0x80) {
            _sda_high(hi2c); // 写入1
        } else {
            _sda_low(hi2c); // 写入0
        }
        delay_us(hi2c->delay_us); // 等待一段时间
        _scl_high(hi2c); // SCL拉高
        delay_us(hi2c->delay_us); // 等待一段时间
        _scl_low(hi2c); // SCL拉低
        byte <<= 1; // 左移一位
    }
    _sda_high(hi2c); // 释放SDA线
    // 等待ACK
    uint8_t ack = SoftI2C_WaitAck(hi2c, 1);
    if (ack != HAL_OK) {
        return HAL_ERROR; // 返回0表示写入失败
    }

    return HAL_OK;;
}
uint8_t SoftI2C_ReadByte(SoftwareI2C_HandleTypeDef* hi2c, uint8_t ack)
{
    uint8_t byte = 0;
    _sda_input(hi2c); // 设置SDA为输入
    for (int i = 0; i < 8; i++) {
        _scl_low(hi2c); // SCL拉低
        delay_us(hi2c->delay_us); // 等待一段时间
        _scl_high(hi2c); // SCL拉高
        delay_us(hi2c->delay_us); // 等待一段时间
        byte <<= 1; // 左移一位
        if (HAL_GPIO_ReadPin(hi2c->sda_port, hi2c->sda_pin)) {
            byte |= 0x01; // 如果SDA为高电平，设置最低位为1
        }
    }

    if (ack) {
        SoftI2C_SendAck(hi2c);
    } else {
        SoftI2C_SendNAck(hi2c); // 发送NACK
    }
    delay_us(hi2c->delay_us); // 等待一段时间
    _scl_high(hi2c); // SCL拉高以结束传输
    delay_us(hi2c->delay_us); // 等待一段时间
    return byte;
}

HAL_StatusTypeDef SoftI2C_Mem_Write(
    SoftwareI2C_HandleTypeDef* hi2c,
    uint16_t dev_addr,
    uint16_t mem_addr,
    uint16_t mem_addr_size,
    uint8_t* data,
    uint16_t size)
{
    SoftI2C_Start(hi2c);
    if (HAL_OK != SoftI2C_WriteByte(hi2c, dev_addr << 1))
        return HAL_ERROR;

    // 写入内存地址
    if (mem_addr_size == I2C_MEMADD_SIZE_16BIT) {
        if (HAL_OK != SoftI2C_WriteByte(hi2c, (uint8_t)(mem_addr >> 8)))
            return HAL_ERROR;
    }
    if (HAL_OK != SoftI2C_WriteByte(hi2c, (uint8_t)mem_addr))
        return HAL_ERROR;

    // 写入数据
    for (uint16_t i = 0; i < size; i++) {
        if (HAL_OK != SoftI2C_WriteByte(hi2c, data[i]))
            return HAL_ERROR;
    }

    SoftI2C_Stop(hi2c);
    return HAL_OK;
}

HAL_StatusTypeDef SoftI2C_Mem_Read(
    SoftwareI2C_HandleTypeDef *hi2c,
    uint16_t dev_addr,
    uint16_t mem_addr,
    uint16_t mem_addr_size,
    uint8_t *data,
    uint16_t size
)
{
    // 地址写入阶段
    SoftI2C_Start(hi2c);
    if (HAL_OK != SoftI2C_WriteByte(hi2c, dev_addr << 1))
        return HAL_ERROR;

    // 写入内存地址
    if (mem_addr_size == I2C_MEMADD_SIZE_16BIT) {
        if (HAL_OK != SoftI2C_WriteByte(hi2c, (uint8_t)(mem_addr >> 8)))
            return HAL_ERROR;
    }
    if (HAL_OK != SoftI2C_WriteByte(hi2c, (uint8_t)mem_addr))
        return HAL_ERROR;

    // 重启进行读取
    SoftI2C_Start(hi2c);
    if (HAL_OK != SoftI2C_WriteByte(hi2c, (dev_addr << 1) | 0x01))
        return HAL_ERROR;

    // 读取数据
    for (uint16_t i = 0; i < size; i++) {
        data[i] = SoftI2C_ReadByte(hi2c, i < (size - 1)); // 最后一个字节发NACK
    }

    SoftI2C_Stop(hi2c);
    return HAL_OK;
}

static inline void _sda_output(SoftwareI2C_HandleTypeDef* hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin = hi2c->sda_pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(hi2c->sda_port, &GPIO_InitStruct);
}
static inline void _sda_input(SoftwareI2C_HandleTypeDef* hi2c)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    GPIO_InitStruct.Pin = hi2c->sda_pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(hi2c->sda_port, &GPIO_InitStruct);
}

static inline void _scl_high(SoftwareI2C_HandleTypeDef* hi2c)
{
    HAL_GPIO_WritePin(hi2c->scl_port, hi2c->scl_pin, GPIO_PIN_SET);
}
static inline void _scl_low(SoftwareI2C_HandleTypeDef* hi2c)
{
    HAL_GPIO_WritePin(hi2c->scl_port, hi2c->scl_pin, GPIO_PIN_RESET);
}
static inline void _sda_high(SoftwareI2C_HandleTypeDef* hi2c)
{
    HAL_GPIO_WritePin(hi2c->sda_port, hi2c->sda_pin, GPIO_PIN_SET);
}
static inline void _sda_low(SoftwareI2C_HandleTypeDef* hi2c)
{
    HAL_GPIO_WritePin(hi2c->sda_port, hi2c->sda_pin, GPIO_PIN_RESET);
}
